import Konva, {
    Stage,
    Layer,
    Group,
    Image,
    Rect,
    Path,
    Line,
    Text,
} from "konva";
import scoreUnderline from "../../assets/paperTrace/score-underline.png";
import orderDefaultBg from "../../assets/paperTrace/order-default-bg.png";
import cnSentenceSelectBg from "../../assets/paperTrace/cn-sentence-sel.png";
import cnParagraphSelectBg from "../../assets/paperTrace/cn-paragraph-sel.png";
import enSentenceSelectBg from "../../assets/paperTrace/en-sentence-sel.png";
import { capitalizeWord, isClick, loadImage, sleep } from "./utils";

Konva.showWarnings = false;

// ❌
// TODO:
/**
 * 1. 横向如果单张或两张，宽度小于容器，需居中展示 ✅
 * 2. 生成图片并放大展示 ✅
 * 3. 解决标注层级覆盖问题，展示优化 ✅
 * 4. 序号单独绘制，序号必须在所有标注顶层，仅次于分数 ✅
 * 5. watch与changeMarks的竞态问题 ✅
 * 6. 滑动与缩放事件处理，PC端不需要双指缩放
 * 7. 好像把和文字标注的联动给弄没了 ✅
 * 8. 顶部序号会超出容器范围
 * 9. 分数位置微调 ✅
 */

class EssayEvaluator {
    constructor(options) {
        this.subject = options?.subject || "chinese"; // 学科
        this.dir = options?.dir || "horizontal"; // 当前默认竖向是h5，横向是pc

        this.stage = null; // 舞台
        this.imageLayer = null; // 图片层
        this.imageGroup = null; // 图片组
        this.imageGroupConfig = {}; // 图片组配置
        this.mainLayer = null; // 上层
        this.mainGroup = null; // 上层组
        this.mainGroupConfig = {}; // 上层组配置

        this.allMarkTypes = ["word", "sentence", "paragraph", "correction"]; // 此处不包括分数，分数暂时是固定显示的
        this.wordGroup = null; // 单词组
        this.sentenceGroup = null; // 句子组
        this.paragraphGroup = null; // 段落组
        this.correctionGroup = null; // 智能纠错组
        this.orderNumberGroup = null; // 序号组
        this.scoreGroup = null; // 分数组
        this.wordList = []; // 单词数据列表
        this.sentenceList = []; // 句子数据列表
        this.correctionList = []; // 智能纠错数据列表
        this.paragraphList = []; // 段落数据列表
        this.wordzIndex = 1;
        this.sentencezIndex = 1;
        this.paragraphzIndex = 1;
        this.correctionzIndex = 1;
        this.orderNumberzIndex = 3;
        this.scorezIndex = 5;

        this.scale = 1; // 当前缩放比例
        this.minScale = 1; // 最小缩放比例
        this.maxScale = 3; // 最大缩放比例
        this.isDragging = false; // 是否正在拖动
        this.isPinching = false; // 是否正在缩放
        this.startTouchPos = { x: 0, y: 0 }; // 起始触摸位置
        this.lastTouchPos = { x: 0, y: 0 }; // 上次触摸位置
        this.lastTouchDistance = 0; // 上次触摸距离
        this.lastTouchCenter = { x: 0, y: 0 }; // 上次触摸中心

        // 惯性滑动相关变量
        this.velocityX = 0;
        this.velocityY = 0;
        this.lastMoveTime = 0;
        this.interiaAnimationId = null;

        this.wordRectStrokeWidth = 3; // 单词矩形边框宽度
        this.underlineStrokeWidth = 3; // 波浪线和下划线宽度

        this.renderWordsFunc = this.renderWords.bind(this);
        this.renderSentencesFunc = this.renderSentences.bind(this);
        this.renderParagraphsFunc = this.renderParagraphs.bind(this);
        this.renderCorrectionsFunc = this.renderCorrections.bind(this);
        this.handleTouchstartFunc = this.handleTouchstart.bind(this);
        this.handleTouchmoveFunc = this.handleTouchmove.bind(this);
        this.handleTouchendFunc = this.handleTouchend.bind(this);

        this.onMarkClick = options?.onMarkClick || (() => {});
    }

    initStage(config) {
        const { container, width, height } = config;

        this.stage = new Stage({
            container,
            width,
            height,
        });
    }

    initLayer(config) {
        this.initImageLayer(config);
        this.initMainLayer(config);
    }

    initImageLayer(groupConfig) {
        this.imageLayer = new Layer();
        this.stage.add(this.imageLayer);

        this.imageGroup = new Group(groupConfig);
        this.imageLayer.add(this.imageGroup);

        this.imageGroupConfig = groupConfig;
    }

    initMainLayer(groupConfig) {
        this.mainLayer = new Layer();
        this.stage.add(this.mainLayer);

        this.mainGroup = new Group(groupConfig);
        this.mainLayer.add(this.mainGroup);

        this.mainGroupConfig = groupConfig;
    }

    initMarkGroup() {
        this.wordGroup = new Group({ name: "wordGroup" });
        this.sentenceGroup = new Group({ name: "sentenceGroup" });
        this.paragraphGroup = new Group({ name: "paragraphGroup" });
        this.correctionGroup = new Group({ name: "correctionGroup" });
        this.orderNumberGroup = new Group({
            name: "orderNumberGroup",
        });
        this.mainGroup.add(
            this.wordGroup,
            this.sentenceGroup,
            this.paragraphGroup,
            this.correctionGroup,
            this.orderNumberGroup
        );
    }

    // 高度动态调试适配
    adaptHeight(height) {
        this.stage.height(height);
        this.limitPosition();
    }

    renderBgImages(list) {
        for (let i = 0; i < list.length; i++) {
            const { img, x, y, width, height } = list[i];
            const image = new Image({
                x,
                y,
                width,
                height,
                image: img,
            });
            this.imageGroup.add(image);
        }
    }

    renderMask(config) {
        const { mainGroup } = this;
        const mask = new Rect({
            x: 0,
            y: 0,
            width: mainGroup.width(),
            height: mainGroup.height(),
            fill: "rgba(0,0,0,0.2)",
            ...config,
        });
        mainGroup.add(mask);
    }

    renderWords(list = []) {
        for (let i = 0; i < list.length; i++) {
            const item = list[i];
            const { isHighlight, isEmpty, cooridates, index } = item;
            if (isEmpty) continue;
            const group = new Group({
                name: "wordGroupItem",
                index,
            });
            for (let j = 0; j < cooridates.length; j++) {
                const cooridate = cooridates[j];
                const { x, y, width, height } = cooridate;
                const rectInteraction = createInteractionRect({
                    name: "interaction",
                    ...cooridate,
                    opacity: isHighlight ? 1 : 0,
                });
                const rectFrame = new Rect({
                    x,
                    y,
                    width,
                    height,
                    stroke: "#FFB10B",
                    strokeWidth: this.wordRectStrokeWidth,
                });
                group.add(rectInteraction);
                group.add(rectFrame);
            }
            this.wordGroup.add(group);
        }
        this.wordList = list;
    }

    renderSentences(list = []) {
        for (let i = 0; i < list.length; i++) {
            const item = list[i];
            const { isHighlight, isEmpty, cooridates, index } = item;
            if (isEmpty) continue;
            const group = new Group({
                name: "sentenceGroupItem",
                index,
            });

            const interactionGroup = new Group({
                name: "interactionGroup",
                opacity: isHighlight ? 1 : 0,
            }); // 高亮交互组
            const waveLineGroup = new Group({ name: "waveLineGroup" }); // 波浪线组

            for (let j = 0; j < cooridates.length; j++) {
                const cooridate = cooridates[j];
                const { x, y, width, height } = cooridate;
                const rectInteraction = createInteractionRect({
                    name: "interaction",
                    ...cooridate,
                });
                interactionGroup.add(rectInteraction);

                const wavePath = new Path(
                    createWaveLineConfig({
                        x,
                        y,
                        width,
                        height,
                        strokeWidth: this.underlineStrokeWidth,
                        stroke: "#23E067",
                    })
                );
                waveLineGroup.add(wavePath);
            }
            group.add(interactionGroup);
            group.add(waveLineGroup);
            this.sentenceGroup.add(group);
        }
        this.sentenceList = list;
    }
    // 渲染段落
    renderParagraphs(list = []) {
        for (let i = 0; i < list.length; i++) {
            const item = list[i];
            const { isHighlight, isEmpty, cooridates, index } = item;
            if (isEmpty) continue;
            const group = new Group({
                name: "paragraphGroupItem",
                index,
            });

            const interactionGroup = new Group({
                opacity: isHighlight ? 1 : 0,
            }); // 高亮交互组
            const underLineGroup = new Group();

            for (let j = 0; j < cooridates.length; j++) {
                const cooridate = cooridates[j];
                const rectInteraction = createInteractionRect({
                    name: "interaction",
                    ...cooridate,
                });
                interactionGroup.add(rectInteraction);

                const underLine = new Line(
                    createUnderLineConfig({
                        ...cooridate,
                        strokeWidth: this.underlineStrokeWidth,
                    })
                );
                underLineGroup.add(underLine);

                group.add(interactionGroup);
                group.add(underLineGroup);
                this.paragraphGroup.add(group);
            }
        }
        this.paragraphList = list;
    }
    // 渲染智能纠错
    renderCorrections(list = []) {
        for (let i = 0; i < list.length; i++) {
            const item = list[i];
            const { isHighlight, isEmpty, cooridates, index } = item;
            if (isEmpty) continue;
            const group = new Group({
                name: "correctionGroupItem",
                index,
            });

            const interactionGroup = new Group({
                name: "interactionGroup",
                opacity: isHighlight ? 1 : 0,
            }); // 高亮交互组
            const waveLineGroup = new Group({ name: "waveLineGroup" }); // 波浪线组

            for (let j = 0; j < cooridates.length; j++) {
                const cooridate = cooridates[j];
                const { x, y, width, height } = cooridate;
                const rectInteraction = createInteractionRect({
                    name: "interaction",
                    ...cooridate,
                });
                interactionGroup.add(rectInteraction);

                const wavePath = new Path(
                    createWaveLineConfig({
                        x,
                        y,
                        width,
                        height,
                        strokeWidth: this.underlineStrokeWidth,
                        stroke: "#FF1717",
                    })
                );
                waveLineGroup.add(wavePath);
            }
            group.add(interactionGroup);
            group.add(waveLineGroup);
            this.correctionGroup.add(group);
        }
        this.correctionList = list;
    }
    // 渲染指定类型mark的序号
    async renderOrderNumber(type) {
        const list = this[`${type}List`].filter((item) => !item.isEmpty);
        if (!list || !list.length) return;
        const groups = [];
        for (let i = 0; i < list.length; i++) {
            const item = list[i];
            const index = item.index;
            // 将列表过滤后，长度应该是一致的，用索引即可
            const { isHighlight, cooridates } = item;
            const cooridate = cooridates[0];
            const { x, y } = cooridate;
            const bgImg = await this.getOrderNumberBg(type, isHighlight);
            const height = 30;
            const width = (height * bgImg.width) / bgImg.height;
            const group = new Group({
                // name: `${type}OrderNumberGroup`,
                name: `orderNumberItem`,
                x: x - width / 2,
                y: y - height / 2,
                visible: true,
                index,
            });
            const bgImage = new Image({
                image: bgImg,
                x: 0,
                y: 0,
                width,
                height: height,
            });
            const text = new Text({
                x: 0,
                y: -1,
                width,
                height: height,
                align: "center",
                verticalAlign: "middle",
                text: index, // 这里按真实渲染的mark序号
                fontSize: width / 1.5,
                fill: "#fff",
            });
            group.add(bgImage);
            group.add(text);
            this.orderNumberGroup.add(group);
            groups.push(group);
        }
        this.mainLayer.batchDraw();
        for (let i = 0; i < list.length; i++) {
            if (list[i].isHighlight) {
                groups[i].zIndex(3);
            }
        }
    }
    // 获取序号背景图片
    async getOrderNumberBg(type, isHighlight) {
        if (type === "paragraph") {
            return await loadImage(
                isHighlight ? cnParagraphSelectBg : orderDefaultBg
            );
        } else if (type === "sentence") {
            return await loadImage(
                isHighlight ? cnSentenceSelectBg : orderDefaultBg
            );
        } else if (type === "correction") {
            return await loadImage(
                isHighlight ? enSentenceSelectBg : orderDefaultBg
            );
        }
    }
    clearAllOrderNumber() {
        // this.allMarkTypes.forEach((type) => {
        //     this.clearOrderNumber(type);
        // });
        this.orderNumberGroup.destroyChildren();
    }
    // 清除指定类型mark的序号
    // clearOrderNumber(...types) {
    // const topGroup = this.orderNumberGroup;
    // topGroup.destroyChildren();
    // for (let type of types) {
    // const typeChildren = topGroup.children.filter((node) => {
    //     return node.name() === `${type}OrderNumberGroup`;
    // });
    // const len = typeChildren.length;
    // for (let i = 0; i < len; i++) {
    //     const orderNumberGroup = typeChildren[i];
    //     orderNumberGroup && orderNumberGroup.destroy();
    // }
    // }
    // }
    // 渲染分数
    async renderScore(score) {
        if (!this.scoreGroup) {
            this.scoreGroup = new Group({
                name: "scoreGroup",
            });
            this.mainGroup.add(this.scoreGroup);
        }
        const scoreText = new Text({
            fontSize: 50,
            fontFamily: "Muyao",
            fill: "#D81E06",
            listening: false,
            text: score,
        });

        const scoreUnderlineImage = await loadImage(scoreUnderline);
        const imageConfig = this.calUnderlineImageConfig(
            scoreText,
            scoreUnderlineImage
        );
        const image = new Image(imageConfig);
        // 分数默认渲染在右上角
        this.scoreGroup.x(this.mainGroup.width() - image.width());
        this.scoreGroup.add(image);
        this.scoreGroup.add(scoreText);
        this.scoreGroup.zIndex(this.scorezIndex);
    }
    // 计算分数下划线图片的配置
    calUnderlineImageConfig(scoreText, img) {
        const scale = 1.8;
        const scoreTextWidth = scoreText.width();
        const scoreTextHeight = scoreText.height();
        const underlineWidth = scoreTextWidth * scale;
        const underlineHeight = underlineWidth * (img.height / img.width);
        const imageConfig = {
            image: img,
            x: -scoreTextWidth / scale + 10,
            y: scoreTextHeight,
            width: underlineWidth,
            height: underlineHeight,
        };
        return imageConfig;
    }
    // 绑定标注事件
    bindMarkEvent(...types) {
        for (let i = 0; i < types.length; i++) {
            const type = types[i];
            const group = this[`${type}Group`];

            group && this.bindTapClickEvent(group);
        }
    }
    unBindAllMarkEvent() {
        this.allMarkTypes.forEach((type) => {
            this.unBindMarkEvent(type);
        });
    }
    // 解绑标注事件
    unBindMarkEvent(...types) {
        for (let i = 0; i < types.length; i++) {
            const type = types[i];
            const group = this[`${type}Group`];
            const func = this[`render${capitalizeWord(type)}sFunc`];

            group && group.off("tap click", func);
        }
    }
    // 绑定点击事件
    bindTapClickEvent(node) {
        const markType = node.name().slice(0, -5);
        node.on("tap click", (e) => {
            const touches = this.getEventTouches(e);
            if (
                !isClick(this.startTouchPos, {
                    x: touches[0].clientX,
                    y: touches[0].clientY,
                })
            ) {
                return;
            }
            const groupItem = findUpwardNode(e.target, `${node.name()}Item`);
            if (groupItem) {
                this.highlightMark(markType, groupItem.attrs.index);
                // 触发回调
                this.onMarkClick &&
                    this.onMarkClick(markType, groupItem.attrs.index);
            }
        });
    }
    // 绑定滑动和缩放事件
    bindDragAndScaleEvent() {
        const { stage } = this;
        stage.on("touchstart mousedown", this.handleTouchstartFunc);
        stage.on("touchmove mousemove", this.handleTouchmoveFunc);
        stage.on("touchend mouseup", this.handleTouchendFunc);
        stage.on("touchcancel mouseleave", this.handleTouchendFunc);
    }
    // 解绑滑动和缩放事件
    unBindDragAndScaleEvent() {
        const { stage } = this;
        stage.off("touchstart mousedown", this.handleTouchstartFunc);
        stage.off("touchmove mousemove", this.handleTouchmoveFunc);
        stage.off("touchend mouseup", this.handleTouchendFunc);
        stage.off("touchcancel mouseleave", this.handleTouchendFunc);
    }
    // 处理事件兼容
    getEventTouches(e) {
        // return this.dir === "vertical" ? e.evt.touches : [e.evt];
        return e.evt.changedTouches || e.evt.touches || [e.evt];
    }
    // 处理触摸开始事件
    handleTouchstart(e) {
        e.evt.preventDefault(); // 阻止默认行为

        // 如果有惯性动画正在进行，取消它
        if (this.inertiaAnimationId !== null) {
            cancelAnimationFrame(this.inertiaAnimationId);
            this.inertiaAnimationId = null;
        }

        const touches = this.getEventTouches(e);
        if (touches.length === 1) {
            // 单指触摸 - 准备拖动
            this.isDragging = true;
            this.isPinching = false;
            // 记录触摸起始位置
            this.lastTouchPos = this.startTouchPos = {
                x: touches[0].clientX,
                y: touches[0].clientY,
            };

            this.lastMoveTime = Date.now();
            this.velocityX = 0;
            this.velocityY = 0;
        } else if (touches.length === 2) {
            // 双指触摸 - 准备缩放
            this.isPinching = true;
            this.isDragging = false;

            // 计算两指间的距离和中心点
            const touch1 = touches[0];
            const touch2 = touches[1];

            this.lastTouchCenter = {
                x: (touch1.clientX + touch2.clientX) / 2,
                y: (touch1.clientY + touch2.clientY) / 2,
            };

            this.lastTouchDistance = Math.sqrt(
                Math.pow(touch2.clientX - touch1.clientX, 2) +
                    Math.pow(touch2.clientY - touch1.clientY, 2)
            );
        }
    }
    // 处理触摸移动事件
    handleTouchmove(e) {
        e.evt.preventDefault(); // 阻止默认行为

        const {
            mainLayer,
            imageLayer,
            mainGroup,
            imageGroup,
            lastTouchPos,
            lastTouchCenter,
            lastTouchDistance,
            maxScale,
            minScale,
            scale,
        } = this;
        const touches = this.getEventTouches(e);
        if (touches.length === 1 && this.isDragging) {
            // 单指触摸移动 - 处理拖动
            const currentPos = {
                x: touches[0].clientX,
                y: touches[0].clientY,
            };

            // 计算移动距离
            const dx = currentPos.x - lastTouchPos.x;
            const dy = currentPos.y - lastTouchPos.y;
            const tox = imageGroup.x() + dx;
            const toy = imageGroup.y() + dy;

            const currentTime = Date.now();
            const timeElapsed = currentTime - this.lastMoveTime;

            // 计算速度 (像素/毫秒)
            if (timeElapsed > 0) {
                this.velocityX = dx / timeElapsed;
                this.velocityY = dy / timeElapsed;
            }

            // 应用移动
            imageGroup.position({
                x: tox,
                y: toy,
            });

            // 同步遮罩的移动
            mainGroup.position({
                x: tox,
                y: toy,
            });

            // 更新上一次触摸位置
            this.lastTouchPos = currentPos;
            this.lastMoveTime = currentTime;

            // 应用边界限制
            this.limitPosition();
            imageLayer.batchDraw();
            mainLayer.batchDraw();
        } else if (touches.length === 2 && this.isPinching) {
            // 双指触摸移动 - 处理缩放
            const touch1 = touches[0];
            const touch2 = touches[1];

            // 计算新的中心点和距离
            const currentCenter = {
                x: (touch1.clientX + touch2.clientX) / 2,
                y: (touch1.clientY + touch2.clientY) / 2,
            };

            const currentDistance = Math.sqrt(
                Math.pow(touch2.clientX - touch1.clientX, 2) +
                    Math.pow(touch2.clientY - touch1.clientY, 2)
            );

            if (lastTouchDistance > 0) {
                // 计算缩放因子
                const scaleFactor = currentDistance / lastTouchDistance;

                // 计算新的缩放比例
                const newScale = Math.max(
                    minScale,
                    Math.min(maxScale, scale * scaleFactor)
                );

                if (newScale !== scale) {
                    // 计算缩放中心点在图片组上的相对位置
                    const centerPointOnGroup = {
                        x: (lastTouchCenter.x - imageGroup.x()) / scale,
                        y: (lastTouchCenter.y - imageGroup.y()) / scale,
                    };

                    // 应用新的缩放比例
                    imageGroup.scale({ x: newScale, y: newScale });
                    mainGroup.scale({ x: newScale, y: newScale });

                    // 调整位置，使缩放中心点保持在双指中心
                    const posX =
                        currentCenter.x - centerPointOnGroup.x * newScale;
                    const posY =
                        currentCenter.y - centerPointOnGroup.y * newScale;
                    imageGroup.position({
                        x: posX,
                        y: posY,
                    });
                    mainGroup.position({
                        x: posX,
                        y: posY,
                    });

                    this.scale = newScale;
                }

                // 处理平移
                const dx = currentCenter.x - lastTouchCenter.x;
                const dy = currentCenter.y - lastTouchCenter.y;

                if (dx !== 0 || dy !== 0) {
                    const tx = imageGroup.x() + dx;
                    const ty = imageGroup.y() + dy;
                    imageGroup.position({
                        x: tx,
                        y: ty,
                    });
                    // 同步遮罩的平移
                    mainGroup.position({
                        x: tx,
                        y: ty,
                    });
                }

                // 更新上一次的中心点和距离
                this.lastTouchCenter = currentCenter;
                this.lastTouchDistance = currentDistance;

                // 应用边界限制
                this.limitPosition();
                imageLayer.batchDraw();
                mainLayer.batchDraw();
            }
        }
    }
    // 处理触摸结束事件
    handleTouchend(e) {
        const touches = this.getEventTouches(e);
        if (touches.length === 1) {
            // 从双指变为单指
            this.isPinching = false;
            this.isDragging = false;

            // 记录新的触摸位置
            this.lastTouchPos = {
                x: touches[0].clientX,
                y: touches[0].clientY,
            };

            // 应用边界限制
            this.limitPosition();
            this.imageLayer.batchDraw();
            this.mainLayer.batchDraw();
        } else {
            if (
                this.isDragging &&
                (Math.abs(this.velocityX) > 0.05 ||
                    Math.abs(this.velocityY) > 0.05)
            ) {
                this.startInertiaAnimation();
            }

            this.isPinching = false;
            this.isDragging = false;
            this.lastTouchCenter = { x: 0, y: 0 };
            this.lastTouchDistance = 0;
        }
    }
    // 限制滑动边界
    limitPosition() {
        const { imageGroup, mainGroup, stage, scale } = this;
        const groupWidth = imageGroup.width() * scale;
        const groupHeight = imageGroup.height() * scale;
        const stageWidth = stage.width();
        const stageHeight = stage.height();

        let x = imageGroup.x();
        let y = imageGroup.y();

        // 水平方向限制
        if (groupWidth <= stageWidth) {
            // 如果图片组宽度小于舞台宽度，则水平居中
            x = (stageWidth - groupWidth) / 2;
        } else {
            // 否则限制在边界内
            const minX = stageWidth - groupWidth;
            const maxX = 0;
            x = Math.max(minX, Math.min(maxX, x));
        }

        // 垂直方向限制
        if (groupHeight <= stageHeight) {
            // 如果图片组高度小于舞台高度，则垂直居中
            y = (stageHeight - groupHeight) / 2;
        } else {
            // 否则限制在边界内
            const minY = stageHeight - groupHeight;
            const maxY = 0;
            y = Math.max(minY, Math.min(maxY, y));
        }

        // 应用新位置
        imageGroup.position({ x, y });
        mainGroup.position({ x, y });
    }
    // 惯性滑动函数
    startInertiaAnimation() {
        const friction = 0.95; // 摩擦系数，数值越小减速越快
        const minVelocity = 0.01; // 最小速度阈值，低于此值停止动画

        const animate = () => {
            const {
                mainLayer,
                imageLayer,
                imageGroup,
                mainGroup,
                velocityX,
                velocityY,
            } = this;

            // 如果速度足够小，停止动画
            if (
                Math.abs(velocityX) < minVelocity &&
                Math.abs(velocityY) < minVelocity
            ) {
                this.inertiaAnimationId = null;
                return;
            }

            // 应用移动
            imageGroup.position({
                x: imageGroup.x() + velocityX * 16, // 假设16ms一帧
                y: imageGroup.y() + velocityY * 16,
            });

            // 同步遮罩的移动
            mainGroup.position({
                x: mainGroup.x() + velocityX * 16,
                y: mainGroup.y() + velocityY * 16,
            });

            // 应用边界限制
            this.limitPosition();

            // 应用摩擦力减速
            this.velocityX *= friction;
            this.velocityY *= friction;

            // 绘制图层
            imageLayer.batchDraw();
            mainLayer.batchDraw();

            // 继续动画
            this.inertiaAnimationId = requestAnimationFrame(animate);
        };

        this.inertiaAnimationId = requestAnimationFrame(animate);
    }
    // 清除全部mark，不保留序号和分数
    clearAllMark() {
        this.allMarkTypes.forEach((type) => {
            this.clearMark(type);
        });
    }
    // 清除mark
    clearMark(...types) {
        for (let i = 0; i < types.length; i++) {
            const type = types[i];
            const group = this[`${type}Group`];
            const list = this[`${type}List`];

            if (group) {
                // 删除所有子元素
                group.destroyChildren();
            }
            if (list) {
                // 去除高亮状态
                list.forEach((item) => (item.isHighlight = false));
            }
        }
    }
    highlightMark(type, index) {
        const group = this[`${type}Group`];
        const list = this[`${type}List`];
        const renderFunc = this[`render${capitalizeWord(type)}sFunc`];
        const repelType =
            type === "word" ? "sentence" : type === "sentence" ? "word" : "";

        if (!group || !list || !list.length || typeof renderFunc !== "function")
            return;

        list.forEach((item) => {
            item.isHighlight = false;
            if (item.index === index && !item.isEmpty) {
                item.isHighlight = true;
            }
        });

        group.destroyChildren();
        renderFunc && renderFunc(list);
        if (type !== "word") {
            this.clearAllOrderNumber();
            this.renderOrderNumber(type);
        }
        // 处理单词和句子
        if (repelType) {
            this.cancelMarkHighlight(repelType);
            if (repelType === "sentence") {
                this.clearAllOrderNumber();
                this.renderOrderNumber(repelType);
            }
        }
    }
    // 取消高亮并重新渲染，不包括序号
    cancelMarkHighlight(type, reRender = true) {
        const group = this[`${type}Group`];
        const list = this[`${type}List`];
        const renderFunc = this[`render${capitalizeWord(type)}sFunc`];

        if (!list || !group || !renderFunc) return;

        list.forEach((item) => (item.isHighlight = false));
        group.destroyChildren();
        reRender && renderFunc(list);
    }
    // 将内容滚动到视口范围内
    scrollToView(markType, index) {
        if (this.dir === "vertical") {
            this.scrollToViewVertical(markType, index);
        } else {
            // 暂不让横向滚动
            // this.scrollToViewHorizontal(markType, index);
        }
    }
    scrollToViewVertical(markType, index) {
        const { stage, mainGroup, imageGroup, scale } = this;
        const list = this[`${markType}List`];
        const item = list.find((item) => item.index === index && !item.isEmpty);
        if (!item) return;
        const nodeY = item.cooridates[0].y * scale;
        const stageHeight = stage.height();
        const scrollY = mainGroup.y() * scale;
        const middleY = Math.abs(scrollY) + stageHeight / 3;
        const diffY = nodeY - middleY;
        const maxY = 0;
        const minY = stageHeight - mainGroup.height() * scale;
        const targetY = Math.max(minY, Math.min(maxY, scrollY - diffY));

        mainGroup.to({ y: targetY, duration: 0.3 });
        imageGroup.to({ y: targetY, duration: 0.3 });
    }
    //TODO:位置计算的不对
    scrollToViewHorizontal(markType, index) {
        const { stage, mainGroup, imageGroup, scale } = this;
        const list = this[`${markType}List`];
        const nodeX = list[index].cooridates[0].x * scale;
        const stageWidth = stage.width();
        const scrollX = mainGroup.x() * scale;
        const middleX = Math.abs(scrollX) + stageWidth / 3;
        const diffX = nodeX - middleX;
        const maxX = 0;
        const minX = stageWidth - mainGroup.width() * scale;
        const targetX = Math.max(minX, Math.min(maxX, scrollX - diffX));

        mainGroup.to({ x: targetX, duration: 0.3 });
        imageGroup.to({ x: targetX, duration: 0.3 });
    }
    toDataURL() {
        const prevWidth = this.stage.width();
        const prevX = this.imageGroup.x();
        this.imageGroup.x(0);
        this.mainGroup.x(0);
        this.stage.width(this.mainGroup.width());

        const url = this.stage.toDataURL({
            mimeType: "image/jpeg",
            quality: 1,
            pixelRatio: 2,
        });

        this.imageGroup.x(prevX);
        this.mainGroup.x(prevX);
        this.stage.width(prevWidth);

        return url;
    }
}

// 创建高亮交互区域
function createInteractionRect({ x, y, width, height, opacity = 1 }) {
    return new Rect({
        x,
        y,
        width,
        height,
        fill: "rgba(0,0,0,1)",
        globalCompositeOperation: "destination-out",
        opacity,
    });
}
// 创建波浪线
function createWaveLineConfig({
    x: startX,
    y: startY,
    width,
    height,
    strokeWidth,
    ...restConfig
}) {
    // 定义波浪路径参数
    const amplitude = 1.5; // 振幅：波峰的高度
    const frequency = 0.35; // 频率：波的密度（数值越大，波越密集）
    let pathData = "";
    for (let x = startX; x <= startX + width; x++) {
        // 控制路径点的密度
        const y = amplitude * Math.sin(x * frequency);
        const relX = x;
        const relY = startY + y + height;
        pathData += x === 0 ? `M ${relX},${relY}` : ` L ${relX},${relY}`;
    }

    return {
        data: pathData,
        stroke: "#23E067", // 绿色
        strokeWidth,
        lineCap: "round", // 圆角端点
        ...restConfig,
    };
}
// 创建下划线
function createUnderLineConfig({ x, y, width, height, strokeWidth }) {
    const deviation = 0; // 偏差
    const endX = x + width;
    const endY = y + height + deviation;

    return {
        points: [x, endY, endX, endY],
        stroke: "#71F0FF",
        strokeWidth,
    };
}
// 向上级寻找对应 name 的节点
function findUpwardNode(node, name) {
    if (node.name() === name) {
        return node;
    }
    if (node.parent) {
        return findUpwardNode(node.parent, name);
    }
    return null;
}

export default EssayEvaluator;
